#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>

#if 0
/* 设备树 */
eint_keys {
    compatible = "eint-keys";
    led-gpios  = <&gpf 4 GPIO_ACTIVE_LOW>,  /* 新版本gpiod_*访问-->逻辑值 */
                 <&gpf 5 GPIO_ACTIVE_LOW>,
                 <&gpf 6 GPIO_ACTIVE_LOW>;

    eint-pins = <&gpf 0 GPIO_ACTIVE_HIGH>,   /* 旧版本gpio_*访问-->物理值 */
                <&gpf 2 GPIO_ACTIVE_HIGH>, 
                <&gpg 3 GPIO_ACTIVE_HIGH>, 
                <&gpg 11 GPIO_ACTIVE_HIGH>;
                
    interrupts-extended = <&intc 0 0 0 IRQ_TYPE_EDGE_BOTH>,
                          <&intc 0 0 2 IRQ_TYPE_EDGE_BOTH>,
                          <&gpg 3 IRQ_TYPE_EDGE_BOTH>,
                          <&gpg 11 IRQ_TYPE_EDGE_BOTH>;
    pinctrl-names = "default";
    pinctrl-0 = <&eint_leds>;
};


#endif

#define EINT_KEY_IRQ_CNT 4
#define LED_CNT 3

struct eint_key_gpios {
    unsigned int gpio_num;
    char name[16];
};

struct led_gpios_desc {
    struct gpio_desc *desc;
    int statue;
};

// 初始化等待队列
static DECLARE_WAIT_QUEUE_HEAD(eint_key_wait);
static int key_interrupt_flag = 0;

static struct eint_key_gpios key_gpios[EINT_KEY_IRQ_CNT];
static struct led_gpios_desc led_gpios[LED_CNT];

static dev_t eint_devid;                  
static struct cdev eint_cdev;           
static struct class *eint_class;         
static struct device *eint_device;
int eint_keys_count = 0;
int led_count = 0;

static int eint_key_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
    char val = 1;
    int ret = 0;
    if (!key_interrupt_flag) {
        wait_event_interruptible(eint_key_wait, key_interrupt_flag);
    }

    ret = copy_to_user(buf, &val, 1);
    // 重新设置flags
    key_interrupt_flag = 0;
    
    return ret;
}

static struct file_operations eint_key_fops = {
    .owner  =   THIS_MODULE,
    .read   =   eint_key_read,
};

static irqreturn_t eint_keys(int irq, void *dev_id)
{
    int i = 0;
    printk("%s enter, irq: %d, %s\n", __func__, irq, (char *)dev_id);

#if 1
    // key-1
    if (strcmp("key-1", (char *)dev_id) == 0) {
        // 设置灯
        led_gpios[0].statue = !led_gpios[0].statue;
        gpiod_set_value(led_gpios[0].desc, !led_gpios[0].statue);
    } else if (strcmp("key-2", (char *)dev_id) == 0) {
        // 设置灯
        led_gpios[1].statue = !led_gpios[1].statue;
        gpiod_set_value(led_gpios[1].desc, !led_gpios[1].statue);
    } else if (strcmp("key-3", (char *)dev_id) == 0) {
        // 设置灯
        led_gpios[2].statue = !led_gpios[2].statue;
        gpiod_set_value(led_gpios[2].desc, led_gpios[2].statue);
    } else {
        // 获取 key的值
        for (i = 0; i < 4; i++) {
            if (i > 1) {
                printk("eint_key %d value %d\n", i, gpio_get_value(key_gpios[i].gpio_num));
            }
       }
    }
    
    /* 唤醒read等待队列 */
    key_interrupt_flag = 1;
    wake_up_interruptible(&eint_key_wait);
#endif

    return IRQ_HANDLED;
}

/**
 * 当驱动和硬件信息匹配成功之后，就会调用probe函数，驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int eint_key_probe(struct platform_device *pdev)
{
    struct device_node *np = NULL;
    int ret = 0;
    int i = 0;
    char *key_name = NULL;
    
    np = pdev->dev.of_node;
    // 通过platform_device获取设备节点
    if (np == NULL) {
        dev_err(&pdev->dev, "eint_key_probe failed");
        return -EINVAL;
    }

    // 通过旧版gpio_的gpioAPI设置eint_keys的GPIO

    // 通过名字获取描述eint_keys GPIO的大小
    eint_keys_count = of_gpio_named_count(np, "eint-pins");
    if (eint_keys_count < 0) {
        dev_err(&pdev->dev, "of_gpio_count failed");
        return -EINVAL;
    }

    // 通过名字获取描述led GPIO的大小
    led_count = of_gpio_named_count(np, "led-gpios");
    if (led_count < 0) {
        dev_err(&pdev->dev, "of_gpio_count failed");
        return -EINVAL;
    }

    // 获取GPIO number
    // 主中断控制器无法作为GPIO访问s3c
    // 次中断控制器可以作为中断又作为GPIO访问s3c-eint
    for (i = 0; i < eint_keys_count; i++) {
        if (i > 1) {
            key_gpios[i].gpio_num = of_get_named_gpio(np, "eint-pins", i);
            if (key_gpios[i].gpio_num < 0) {
                dev_err(&pdev->dev, "of_get_named_gpio failed");
                dev_err(NULL, "of_get_named_gpio failed");
                return -EINVAL;
            }
            // 申请GPIO
            ret = gpio_request(key_gpios[i].gpio_num, NULL);
            if (ret != 0) {
                dev_err(&pdev->dev, "gpio_request failed");
                dev_err(NULL, "gpio_request failed");
                return -EINVAL;
            }

            // 设置方向
            ret = gpio_direction_input(key_gpios[i].gpio_num);
            if (ret < 0) {
                dev_err(&pdev->dev, "gpio_direction_input failed");
                dev_err(NULL, "gpio_direction_input failed");
                return -EINVAL;
            }
        }
    }

    // 通过新版本gpiod_的gpioAPI设置led的GPIO,只能通过platform平台来获取
    /* gpiod_get只能获取 gpios = <&gpf 4 GPIO_ACTIVE_LOW> */
    /* gpiod_get_index可以获取 *-gpios=<&gpf 4 GPIO_ACTIVE_LOW>, <&gpf 5 GPIO_ACTIVE_LOW>; */
    for (i = 0; i < led_count; i++) {
        led_gpios[i].desc = gpiod_get_index(&pdev->dev, "led", i, 0);
        if (IS_ERR(led_gpios[i].desc)) {
            dev_err(&pdev->dev, "gpiod_get_index failed");
            ret = PTR_ERR(led_gpios[i].desc);
            return ret;
        }

        /* 设置方向 */
        gpiod_direction_output(led_gpios[i].desc, 0);
        led_gpios[i].statue = 0;
    }    

    // 通过irq_of_parse_and_map节点获取中断号
    for (i = 0; i < EINT_KEY_IRQ_CNT; i++) {
        // 获取中断资源
        ret = irq_of_parse_and_map(np, i);
        if (ret <= 0) {
            dev_err(&pdev->dev, "cannot find irq index %d\n", i);
            return ret;
        }

        key_name = kasprintf(GFP_KERNEL, "key-%d", i+1);

        // 注册中断号
        ret = devm_request_irq(&pdev->dev, ret, eint_keys, 0, key_name, key_name);
        if (ret != 0) {
            dev_err(&pdev->dev, "cannot claim irq %d\n", ret);
            dev_err(NULL, "cannot claim irq %d\n", ret);
            return ret;
        }
    }

    /* 4. 动态分配字符设备号 */
    alloc_chrdev_region(&eint_devid, 0, 1, "eint_key");

    /* 5.初始化字符设备，添加字符设备 */
    cdev_init(&eint_cdev, &eint_key_fops);
    cdev_add(&eint_cdev, eint_devid, 1);


    /* 6.创建类,它会在sys目录下创建/sys/class/dtsled这个类  */
    eint_class = class_create(THIS_MODULE, "eint_key_c");
    if (IS_ERR(eint_class)) {
        dev_err(&pdev->dev, "create class failed\n");
        ret = PTR_ERR(eint_class);
        return -ret;
    }

    /* 7. 在/sys/class/led下创建dtsled设备，然后mdev通过这个自动创建/dev/dtsled这个设备节点 */
    eint_device = device_create(eint_class, NULL, eint_devid, NULL, "eint_key");
    if (IS_ERR(eint_device)) {
         dev_err(&pdev->dev, "create device failed\n");
         ret = PTR_ERR(eint_device);
        return -ret;
    }

    return 0;
}

/**
 * 硬件信息被移除了，或者驱动被卸载了，全部要释放，释放资源的操作就放在该函数中
 */
static int eint_key_remove(struct platform_device *pdev)
{
    int i = 0;

    // 释放gpio
    for (i = 0; i < eint_keys_count; i++) {
        if (i > 1) {
            gpio_free(key_gpios[i].gpio_num);
        }
    }

    for (i = 0; i < led_count; i++) {
        gpiod_put(led_gpios[i].desc);
    }

    
    /* 注销类、以及类设备 */
    device_destroy(eint_class, eint_devid);
    class_destroy(eint_class);

    /* 删除设备，卸载注册的设备编号 */
    cdev_del(&eint_cdev);
    unregister_chrdev_region(eint_devid, 1);

    return 0;
}

/**
 * 用于设备树匹配
 */
static const struct of_device_id eint_key_dt_match[] = {
    { .compatible = "eint-keys", },
    {},
};

/**
 * platform驱动
 */
static struct platform_driver eint_key_driver = {
    .probe = eint_key_probe,
    .remove = eint_key_remove,
    .driver = {
        .name = "eint_key",
        .of_match_table = eint_key_dt_match,  // 匹配列表
    }
};


static int eint_key_init(void)
{
    int ret = platform_driver_register(&eint_key_driver);
    if (ret < 0) {
        pr_err("eint_key_init platform_driver_register failed");
        return -EINVAL;
    }

    return 0;
}

/**
 * platform驱动模块出口
 */
static void eint_key_exit(void)
{
    platform_driver_unregister(&eint_key_driver);
}

module_init(eint_key_init);
module_exit(eint_key_exit);

MODULE_AUTHOR("huang");
MODULE_DESCRIPTION("jz2440 eint key driver");
MODULE_LICENSE("GPL");

